﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using NPOI.HSSF.UserModel;
using NPOI.XSSF.UserModel;
using NPOI.SS.UserModel;
using ThreadState = System.Threading.ThreadState;
//using NPOI.POIFS.FileSystem;
//using NPOI.Util;


namespace InternetCheck
{
    public partial class Form1 : Form
    {
        #region 变量和属性
        // 文件流
        private FileStream file;

        // Excel工作簿
        private IWorkbook workbook;

        // 工作表
        private ISheet sheet;

        // Internet检查
        private InternetCheck internetCheck;

        // 总表
        private Dictionary<string, InternetCheck.ResultType> webDic;

        // 工作线程
        private Thread processThread;

        // 网页加载完毕
        private bool isComplete = true;

        // 网页标题
        private string title = string.Empty;

        // 停止
        private bool stop = false;

        // 互斥锁
        private Mutex mutex = new Mutex();
        #endregion

        #region 委托和事件
        public delegate void ProcessBeginHandler();
        public delegate void ProcessEndHandler();
        public delegate void UpdateResultHandler(string res, bool ret, bool scroll);
        public delegate void NavigateHandler(string url);
        public ProcessBeginHandler ProcessBeginEvent;
        public ProcessEndHandler ProcessEndEvent;
        public UpdateResultHandler UpdateResultEvent;
        public NavigateHandler NavigeteEvent;
        #endregion

        #region Windows系统dll引用，解决WebBrowser占用内存的问题
        [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        internal static extern bool SetProcessWorkingSetSize(IntPtr pProcess, int dwMinimumWorkingSetSize, int dwMaximumWorkingSetSize);

        [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        internal static extern IntPtr GetCurrentProcess();
        #endregion

        public Form1()
        {
            InitializeComponent();
            internetCheck = new InternetCheck();
            webDic = new Dictionary<string, InternetCheck.ResultType>();
            // 开辟后台线程（不启动）
            processThread = new Thread(FileProcess) { IsBackground = true };
            ProcessBeginEvent = new ProcessBeginHandler(ProcessBegin);
            ProcessEndEvent = new ProcessEndHandler(ProcessEnd);
            UpdateResultEvent = new UpdateResultHandler(UpdateResult);
            NavigeteEvent = new NavigateHandler(Navigate);
#if DEBUG
            this.richTextBoxXlsFile.Text = @"E:\My program\Projects\网站访问检验\Test\河北区2020年2月2日至2月9日发现清理“翻墙”工具信息工作考核表.xls";
#endif
        }

        private void buttonWeiboCheck_Click(object sender, EventArgs e)
        {
            // 处理文件
            if (!processThread.IsAlive)
            {
                if (!OpenXlsFile(richTextBoxXlsFile.Text))
                    return;
                mutex.WaitOne();
                stop = false;
                mutex.ReleaseMutex();
                processThread = new Thread(FileProcess) { IsBackground = true };
                processThread.Start();
            }
            // 停止处理
            else
            {
                DialogResult res = MessageBox.Show("要中止检验吗？",
                    "中止", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (res == DialogResult.Yes)
                {
                    mutex.WaitOne();
                    stop = true;
                    mutex.ReleaseMutex();
                    while (processThread.IsAlive)
                    {
                        Application.DoEvents();
                        Thread.Sleep(100);
                    }
                }
            }
        }

        private bool OpenXlsFile(string path)
        {
            try
            {
                file = new FileStream(path, FileMode.Open, FileAccess.Read);
                // 根据扩展名判断Excel版本
                if (file.Name.EndsWith(".xls", StringComparison.OrdinalIgnoreCase))
                    workbook = new HSSFWorkbook(file);
                else if (file.Name.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase))
                    workbook = new XSSFWorkbook(file);
                else
                    workbook = null;
                file.Close();
                if (workbook == null)
                    return false;
                return true;
            }
            catch (Exception ex)
            {
                UpdateResult(ex.Message);
                return false;
            }
        }

        private bool CloseXlsFile(bool save = true)
        {
            try
            {
                string newName = file.Name;
                int index = newName.LastIndexOf('.');
                newName = newName.Substring(0, index) + "(已检)" + newName.Substring(index);
                if (save)
                {
                    FileStream newfile = new FileStream(newName, FileMode.Create, FileAccess.Write);
                    workbook.Write(newfile);
                    UpdateResult("文件已保存！\n");
                    newfile.Close();
                }
                workbook.Close();
                return true;
            }
            catch (Exception ex)
            {
                UpdateResult(ex.Message, false);
                return false;
            }
        }

        private void ProcessBegin()
        {
            // 清除结果显示
            richTextBoxResult.Clear();
            // 运行期间锁死控件
            richTextBoxXlsFile.Enabled = false;
            buttonOpen.Enabled = false;
            buttonWeiboCheck.Text = "停止检查";
        }

        private void ProcessEnd()
        {
            // 关闭并保存文件
            CloseXlsFile();
            // 解锁控件
            richTextBoxXlsFile.Enabled = true;
            buttonOpen.Enabled = true;
            buttonWeiboCheck.Text = "检查微博";
        }

        private void UpdateResult(string result, bool reset = false, bool scrollEnd = true)
        {
            // 刷新显示
            if (string.IsNullOrWhiteSpace(result))
                return;

            // 是否清除旧内容，重新显示
            if (reset)
                richTextBoxResult.Text = result;
            else
                richTextBoxResult.AppendText(result);

            // 滚到最后一行
            if (scrollEnd)
            {
                richTextBoxResult.SelectionStart = richTextBoxResult.TextLength;
                richTextBoxResult.SelectionStart = richTextBoxResult.GetFirstCharIndexOfCurrentLine();
                richTextBoxResult.ScrollToCaret();
            }
        }

        private void Navigate(string url)
        {
            try
            {
                mutex.WaitOne();
                // 等待网页停止
                while (webBrowser1.IsBusy)
                {
                    Application.DoEvents();
                    webBrowser1.Stop();
                }

                // 清理WebBrowser的内存
                // 先切到空地址，自动释放下
                webBrowser1.Navigate("about:blank");
                while (webBrowser1.IsBusy)
                {
                    Application.DoEvents();
                    webBrowser1.Stop();
                }
                // 再强制手动释放
                IntPtr pHandle = GetCurrentProcess();
                SetProcessWorkingSetSize(pHandle, -1, -1);

                // 打开新网页
                webBrowser1.Navigate(url);
                mutex.ReleaseMutex();
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        private void FileProcess()
        {
            try
            {
                Invoke(ProcessBeginEvent);

                webDic.Clear();
                int count = 0;

                int pageNum = workbook.NumberOfSheets;
                string str = string.Format("\"{0}\"中有{1}个工作表\n", file.Name, pageNum);
                Invoke(UpdateResultEvent, new object[] { str, false, true });

                IRow row;
                string valueStr = string.Empty;
                InternetCheck.ResultType resultType;
                ICellStyle styleRed = workbook.CreateCellStyle();
                ICellStyle styleGreen = workbook.CreateCellStyle();

                for (int page = 0; page < pageNum; page++)
                {
                    str = string.Format("正在处理第{0}张工作表\n", page + 1);
                    Invoke(UpdateResultEvent, new object[] { str, false, true });

                    sheet = workbook.GetSheetAt(page);
                    for (int rowIndex = sheet.FirstRowNum; rowIndex <= sheet.LastRowNum; rowIndex++)
                    {
                        row = sheet.GetRow(rowIndex);
                        foreach (ICell cell in row.Cells)
                        {
                            if (stop)
                            {
                                str = "文件处理中止！\n";
                                Invoke(UpdateResultEvent, new object[] { str, false, true });
                                BeginInvoke(ProcessEndEvent);
                                return;
                            }

                            if (cell.CellType == CellType.String)
                                valueStr = cell.StringCellValue;
                            else
                                continue;

                            // 检查字典中有没有现成的，防止重复检验
                            if (webDic.ContainsKey(valueStr))
                                resultType = webDic[valueStr];
                            else
                            {
                                // 尝试检测是否是微博URL
                                resultType = internetCheck.CheckWeibo(valueStr);
                                // 非URL跳下一个
                                if (resultType == InternetCheck.ResultType.NotURL)
                                    continue;
                                // 如果是微博URL，检验能否正确访问
                                else if (resultType == InternetCheck.ResultType.OK)
                                {
                                    mutex.WaitOne();
                                    isComplete = false;
                                    mutex.ReleaseMutex();
                                    // 浏览器开始访问
                                    Invoke(NavigeteEvent, new object[] { valueStr });

                                    // 是否加载完
                                    while (!isComplete)
                                    {
                                        Application.DoEvents();
                                        // 是否停止线程
                                        if (stop)
                                        {
                                            str = "文件处理中止！\n";
                                            Invoke(UpdateResultEvent, new object[] { str, false, true });
                                            BeginInvoke(ProcessEndEvent);
                                            return;
                                        }
                                        // 查询间隔
                                        Thread.Sleep(300);
                                    }

                                    // 如果404错误
                                    if (title.StartsWith("404"))
                                    {
                                        // 将状态改为Forbidden
                                        resultType = InternetCheck.ResultType.Forbidden;
                                    }

                                    // 添加到字典
                                    webDic.Add(valueStr, resultType);
                                }
                            }

                            // 输出结果

                            str = string.Format("Sheet{0}!Cell({1},{2}), count:{3}, site:{4} : ",
                                page, rowIndex, cell.ColumnIndex, ++count, valueStr);
                            Invoke(UpdateResultEvent, new object[] { str, false, true });
                            Debug.WriteLine(str);

                            switch (resultType)
                            {
                                case InternetCheck.ResultType.OK:
                                    str = "success!\n";
                                    styleGreen.CloneStyleFrom(cell.CellStyle);
                                    styleGreen.FillPattern = FillPattern.SolidForeground;
                                    styleGreen.FillForegroundColor = IndexedColors.Green.Index;
                                    cell.CellStyle = styleGreen;
                                    break;

                                case InternetCheck.ResultType.NotFound:
                                    str = "is not found!\n";
                                    break;

                                case InternetCheck.ResultType.NotSite:
                                    str = "is not the target site!\n";
                                    break;

                                case InternetCheck.ResultType.NotWeb:
                                    str = "is not the right web page!\n";
                                    styleRed.CloneStyleFrom(cell.CellStyle);
                                    styleRed.FillPattern = FillPattern.SolidForeground;
                                    styleRed.FillForegroundColor = IndexedColors.Red.Index;
                                    cell.CellStyle = styleRed;
                                    break;

                                case InternetCheck.ResultType.Forbidden:
                                    str = "is forbidden!\n";
                                    styleRed.CloneStyleFrom(cell.CellStyle);
                                    styleRed.FillPattern = FillPattern.SolidForeground;
                                    styleRed.FillForegroundColor = IndexedColors.Red.Index;
                                    cell.CellStyle = styleRed;
                                    break;

                                case InternetCheck.ResultType.NotURL:
                                    str = "is not correct URL";
                                    break;

                                default:
                                    break;
                            }

                            Invoke(UpdateResultEvent, new object[] { str, false, true });
                        }
                    }
                }
                str = "文件处理完毕！\n";
                Invoke(UpdateResultEvent, new object[] { str, false, true });
                BeginInvoke(ProcessEndEvent);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (processThread.IsAlive)
                processThread.Abort();
        }

        private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            mutex.WaitOne();
            isComplete = true;
            title = webBrowser1.DocumentTitle;
            webBrowser1.Stop();
            Debug.WriteLine(webBrowser1.DocumentTitle);
            mutex.ReleaseMutex();
        }

        private void buttonOpen_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog ofd = new OpenFileDialog())
            {
                ofd.Multiselect = false;
                ofd.CheckFileExists = true;
                ofd.Filter = "Excel files (*.xls;*.xlsx)|*.xls;*.xlsx|All files (*.*)|*.*";
                ofd.FilterIndex = 1;
                ofd.RestoreDirectory = true;

                if (DialogResult.OK == ofd.ShowDialog())
                {
                    richTextBoxXlsFile.Text = ofd.FileName;
                }
            }
        }
    }
}
